在 JavaScript 中,this
關鍵字是一個相當重要且常被誤解的概念,對初學者來說是大魔王般的存在,即使是老手有時也會不小心誤判結果。以函式來說,它的值取決於函式是如何呼叫的,並且在不同的區域中,this
的行為可能不同。實際上要立刻完全弄懂 this 的規則是不容易的,而在這篇文章中,將會先探討 this
關鍵字的常見使用情境。
在開始說明如何判斷 this
拿到的值前,我們要先知道 this
是甚麼。簡單來說,this
這個關鍵字代表的值為目前執行環境綁定的實體。例如在全域執行環境中,this 通常指向全域物件,但這是在非嚴格模式下的結果。
// 在非嚴格模式下,
// 1. 在瀏覽器中,輸出為 window 物件
// 2. 在 node.js 環境中,輸出為 global
console.log(this);
"use strict";
console.log(this); // 嚴格模式下,輸出為 undefined
接下來預設都會以瀏覽器環境的非嚴格模式來舉例,請注意在嚴格模式的結果可能有所不同。
初學者可能會認為函式中的 this
會指向該函式,但這是錯誤的概念。實際上在一個函式中,this
與函式如何宣告沒有關係,僅與呼叫方式有關。this
的值取決於呼叫該函式的物件。直接來看例子。
pet1 = 'dog'; // 會在全域物件中新增 pet1 屬性
var pet2 = 'cat'; // 會在全域物件中新增 pet2 屬性
let pet3 = 'rabbit'; // 不會在全域物件中新增屬性
function showPet() {
console.log(this.pet1);
console.log(this.pet2);
console.log(this.pet3);
}
showPet(); // 依序輸出 'dog' 'cat' undefined
上例是在全域中呼叫 showPet(),所以 showPet 裡面的 this
會指向全域物件。
var name = 'Apple';
function showName() {
console.log(this.name);
}
const person = {
name: 'Banana',
showName: showName
};
showName(); // 輸出 'Apple'。這裡的 showName 在全域中,所以 this 指向全域物件
person.showName(); // 輸出 'Banana'。這裡呼叫 person 物件下的 showName,所以 this 指向 person
var anotherFn = person.showName;
anotherFn(); // 輸出 'Apple'。這裡的 anotherFn 在全域中,所以 this 指向全域物件
我們來看更複雜的情況,以下是在一些方法的回調函式中,this
的範例。
const numbers = [1, 2, 3];
numbers.forEach(function (num) {
console.log(num, this); // 這裡的 forEach 的 callback function,this 是全域物件 window
});
const numbers = [1, 2, 3];
const myObj = {
prop: 'Hello',
sayHello: function () {
numbers.forEach(function (num) {
console.log(num, this); // 呼叫 myObj.sayHello() 時,這裡 this 指向 myObj
}, this);
}
};
myObj.sayHello();
但是在非同步方法的回調函式中,this
指向全域物件。
const myObj = {
prop: 'Hello',
sayHello: function () {
setTimeout(function () {
console.log(this); // this 指向全域物件(在瀏覽器中通常是 window)
}, 1000);
}
};
myObj.sayHello(); // 一秒後輸出 window
箭頭函式沒有自己的 this
,我們只需判斷外層執行環境的 this 值。
直接拿上一個例子改用箭頭函式看會有甚麼差別。
我們先將 setTimeout 的 callback 改用箭頭函式:
const myObj = {
prop: 'Hello',
sayHello: function () {
setTimeout(() => {
console.log(this);
}, 1000);
}
};
myObj.sayHello(); // 一秒後輸出 myObj 物件
這個例子中,setTimeout 的 callback 是箭頭函式,所以它的 this
由外層決定,也就是 sayHello 的執行環境。而 sayHello 是傳統函式,它的 this
由呼叫方式決定,因此透過「myObj.sayHello()」呼叫,this
就會指向 myObj。
我們再把 sayHello 也改用箭頭函式:
const myObj = {
prop: 'Hello',
sayHello: () => {
setTimeout(() => {
console.log(this);
}, 1000);
}
};
myObj.sayHello(); // 一秒後輸出全域物件 window
這個例子中,setTimeout 的 callback 是箭頭函式,所以它的 this
由外層決定,也就是 sayHello 的執行環境。而 sayHello 也是箭頭函式,它的 this
也由外層決定,也就是全域執行環境,因此這裡的 this
會指向 全域物件。
今天舉例了 this
的應用及如何判斷它的值,不過本篇文章只舉例了我認為在開發時比較常見的情況,若要瞭解更詳細的規則可參考MDN文件。實際上我們也可以手動改變 this 綁定的參考物件,就留到隔天談吧。那今天就到這邊,明天見~